/*
 * igo: com.cctv.igo.security.service.ManagerAuthnService.java Create On 2011-4-11 涓婂崍11:03:43 By Q-Wang
 * $Revision: 1.2 $
 */
package com.ctvit.framework.security.service;

import java.util.ArrayList;
import java.util.List;

import org.apache.axis.utils.StringUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.encoding.PasswordEncoder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.provisioning.UserDetailsManager;

import com.ctvit.framework.dao.Condition;
import com.ctvit.framework.security.domain.PermAuthnManager;
import com.ctvit.framework.security.domain.PermAuthzRole;
import com.ctvit.framework.security.dto.Manager;
import com.ctvit.framework.service.BaseService;
import com.ctvit.framework.web.context.ContextHolder;


/**
 * @author <a href="mailto:apeidou@gmail.com">Q-Wang</a>
 * 
 */
public class ManagerAuthnService extends BaseService implements
		UserDetailsManager {
	private PasswordEncoder passwordEncoder;

	public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
		this.passwordEncoder = passwordEncoder;
	}
	/**
	 * @see org.springframework.security.core.userdetails.UserDetailsService#loadUserByUsername(java.lang.String)
	 */
	public Manager loadUserByUsername(String username)
			throws UsernameNotFoundException, DataAccessException {
		List<PermAuthnManager> results = genericDao.getByCondition(
				PermAuthnManager.class, Condition.newInstance().add("userId",username), null);
		if (results.isEmpty()) {
			throw new UsernameNotFoundException("Username Not Found:"
					+ username);
		} else if (results.size() > 1) {
			throw new IncorrectResultSizeDataAccessException(
					"More than one user found with name '" + username + "'", 1,
					results.size());
		}
//		AuthorizationService authorizationService = (AuthorizationService)ContextHolder.getBean(AuthorizationService.class);
		PermAuthnManager user = results.get(0);
//		List<PermAuthzRole> roles = authorizationService.selectAllRoleByUser(user.getPk());
		List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
//		for (PermAuthzRole role : roles) {
			authorities.add(new GrantedAuthorityImpl("AUTHENTICATED"));//给所有用户分配这个角色
//		}
		if (("admin").equals(username)){
			authorities.add(new GrantedAuthorityImpl("MANAGER"));//管理员特有角色
		}
		Manager manager = Manager.createManager(user,authorities);
		return manager;
	}

	/**
	 * @see org.springframework.security.provisioning.UserDetailsManager#createUser(org.springframework.security.core.userdetails.UserDetails)
	 */
	public void createUser(UserDetails user) {
		checkUser(user);
		genericDao.insert((Manager) user);
	}

	private void checkUser(UserDetails user) {
		if (user == null) {
			throw new IllegalArgumentException("user must not be null.");
		}
		if (!(user instanceof Manager)) {
			throw new IllegalArgumentException("user must instanceof Manager.");
		}
	}

	/**
	 * @see org.springframework.security.provisioning.UserDetailsManager#updateUser(org.springframework.security.core.userdetails.UserDetails)
	 */
	public void updateUser(UserDetails user) {
		checkUser(user);
		genericDao.updateExclusive((Manager) user, new String[] { "password" });
	}

	/**
	 * @see org.springframework.security.provisioning.UserDetailsManager#deleteUser(java.lang.String)
	 */
	public void deleteUser(String username) {
		if (username == null || StringUtils.isEmpty(username)) {
			throw new IllegalArgumentException("username must not empty.");
		}
		genericDao.deleteBySqlID(
				"security.userDetailsManager.deleteManagerByPrincipal",
				username);
	}

	/**
	 * @see org.springframework.security.provisioning.UserDetailsManager#changePassword(java.lang.String,
	 *      java.lang.String)
	 */
	public void changePassword(String oldPassword, String newPassword) {
		Authentication currentUser = SecurityContextHolder.getContext().getAuthentication();
		if (currentUser == null) {
			// This would indicate bad coding somewhere
			throw new AccessDeniedException(
					"Can't change password as no Authentication object found in context "
							+ "for current user.");
		}

		String username = currentUser.getName();
		AuthenticationManager authenticationManager = (AuthenticationManager)ContextHolder.getBean(AuthenticationManager.class);
		// If an authentication manager has been set, re-authenticate the user
		// with the supplied password.
		if (authenticationManager != null) {
			// logger.debug("Reauthenticating user '"+ username +
			// "' for password change request.");
			authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, oldPassword));
		} else {
			// logger.debug("No authentication manager set. Password won't be re-checked.");
		}
		Manager user = (Manager) loadUserByUsername(username);
		String newPasswordDigest = passwordEncoder.encodePassword(newPassword,null);
		user.setPassword(newPasswordDigest);
		genericDao.updateBySqlStr("update perm_authn_manager set password='"+newPasswordDigest+"' where pk='"+user.getPk()+"'");
//		genericDao.update(user, new String[] { "password" });
		SecurityContextHolder.getContext().setAuthentication(createNewAuthentication(currentUser, newPassword));
	}
	protected Authentication createNewAuthentication(
			Authentication currentAuth, String newPassword) {
		UserDetails user = loadUserByUsername(currentAuth.getName());

		UsernamePasswordAuthenticationToken newAuthentication = new UsernamePasswordAuthenticationToken(
				user, user.getPassword(), user.getAuthorities());
		newAuthentication.setDetails(currentAuth.getDetails());

		return newAuthentication;
	}
	/**
	 * @see org.springframework.security.provisioning.UserDetailsManager#userExists(java.lang.String)
	 */
	public boolean userExists(String username) {
		List<PermAuthnManager> results = genericDao.getByCondition(
				PermAuthnManager.class, Condition.newInstance().add("userId",
						username), null);
		if (results.size() > 1) {
			throw new IncorrectResultSizeDataAccessException(
					"More than one user found with name '" + username + "'", 1,
					results.size());
		}
		return results.size() == 1;
	}

}
